package web

import (
	"fmt"
	"math"
	"net/http"
	"strconv"
	"time"

	"gitee.com/thubcc/mobile/lte"
	"gitee.com/thubcc/mobile/utils"
	"gitee.com/thubcc/rtlmmap"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/common/log"
)

type LTEView struct {
	view      utils.RFInterface
	port      *Port
	Pos       int              `json:"pos"`
	Cid       int              `json:"cid"`
	Afc       bool             `json:"afc"`
	Name      string           `json:"name"`
	Timing    lte.TimingResult `json:"timing"`
	PPM       float64          `json:"ppm"`
	DFR       float64          `json:"dfr"`
	DF        float64          `json:"df"`
	DT        float64          `json:"dt"`
	BeReset   bool             `json:"beReset"`
	OnTrace   bool             `json:"onTrace"`
	FailCount int              `json:"failCount"`
	HasCal    bool             `json:"hasCal"`
	TPLL      lte.PLL          `json:"tpll"`
	FPLL      lte.PLL          `json:"fpll"`
}

func NewLTEV(view utils.RFInterface, port *Port, name string) *LTEView {
	return &LTEView{
		view:      view,
		port:      port,
		Pos:       0,
		Cid:       0,
		Afc:       false,
		Name:      name,
		Timing:    lte.TimingResult{},
		PPM:       0.0,
		DFR:       0.0,
		DF:        0.0,
		DT:        0.0,
		BeReset:   true,
		OnTrace:   false,
		FailCount: 0,
		HasCal:    false,
		TPLL:      lte.PLL{1e-1, 1e-2},
		FPLL:      lte.PLL{1.0, 0.1},
	}
}

func NewLTEVWithHandle(c *gin.Context) *LTEView {
	port, _ := CommonParam(c)
	config := &rtlmmap.DefaultLTEConfig
	v := rtlmmap.NewRtlView(port, config).FTV(port)
	p, err := GlobPorts.GetPort(port)
	if err != nil {
		c.JSON(http.StatusOK, Response{"Can not get rf port", nil})
		return nil
	}
	v.ResetPPM()
	ltev := NewLTEV(v, p, c.Param("handle"))
	c.JSON(http.StatusOK, Response{nil, nil})
	return ltev
}

func (v *LTEView) SearchPSS(c *gin.Context) {
	res := lte.APIPSSSearch(v.view, float64(v.port.FreqCenter))
	v.Pos = res.Pos
	if c.Query("setf") == "true" {
		v.view.PPMF(res.Df, false)
		v.view.Reset()
		res = lte.APIPSSSearch(v.view, float64(v.port.FreqCenter))
		if v.BeReset {
			v.HasCal = (math.Abs(res.Df) < 1.5e3)
		}
		v.BeReset = false
	}

	c.JSON(http.StatusOK, Response{res, nil})
}

func (v *LTEView) ScanRefSym(c *gin.Context) {
	keys := []string{
		"txp",
		"n",
	}
	defaultSet := map[string]int{
		"txp": 0,
		"n":   1,
	}
	d := make([]int, 0)
	for _, v := range keys {
		d = append(d, Str1int(c.DefaultQuery(v, fmt.Sprint(defaultSet[v]))))
	}
	res, err := lte.APIScanRefSym(v.view, float64(v.port.FreqCenter), d[0], d[1])
	if err != nil {
		c.JSON(http.StatusOK, Response{fmt.Sprint(err), err})
	} else {
		v.Cid = res[0].Cid
		v.Pos = res[0].Pos
		if c.Query("setf") == "true" {
			if v.HasCal {
				v.view.SetDf(res[0].Df)
			} else {
				v.view.PPMF(res[0].Df, false)
			}
			v.view.Reset()
		}
		c.JSON(http.StatusOK, Response{res, nil})
	}
}

func (v *LTEView) CellPeak(c *gin.Context) {
	keys := []string{
		"id",
		"txp",
	}
	defaultSet := map[string]int{
		"id":  v.Cid,
		"txp": 0,
	}
	d := make([]int, 0)
	for _, v := range keys {
		d = append(d, Str1int(c.DefaultQuery(v, fmt.Sprint(defaultSet[v]))))
	}
	res := lte.APICellPeak(v.view, d[0], d[1])
	if c.Query("setf") == "true" {
		v.view.FrameStartAdjust(int64(9600 + res.Pos))
		v.view.HardReset()
		v.view.Reset()
		res.Pos = 9600
	}
	v.Pos = res.Pos
	v.Cid = d[0]
	c.JSON(http.StatusOK, Response{res, nil})
}

func (v *LTEView) CellCFO(c *gin.Context) {
	keys := []string{
		"id",
		"txp",
		"pos",
	}
	defaultSet := map[string]int{
		"id":  v.Cid,
		"txp": 0,
		"pos": v.Pos,
	}
	d := make([]int, 0)
	for _, v := range keys {
		d = append(d, Str1int(c.DefaultQuery(v, fmt.Sprint(defaultSet[v]))))
	}
	res := lte.APICellCFO(v.view, d[0], d[1], d[2])
	if c.Query("setf") == "true" {
		if v.HasCal {
			v.view.SetDf(res.Df)
		} else {
			v.view.PPMF(res.Df, false)
		}
		v.view.Reset()
	}
	c.JSON(http.StatusOK, Response{res, nil})
}

func getFloat(n string, c *gin.Context) (v float64, err error) {
	s := c.Query(n)
	if s == "" {
		err = fmt.Errorf("Not found")
	} else {
		v, err = strconv.ParseFloat(s, 64)
	}
	log.Info(n, v, err)
	return
}
func (v *LTEView) AFC(c *gin.Context) {
	if c.Query("on") == "true" {
		v.Afc = false
		time.Sleep(time.Second)
		v.Afc = true
		if x, err := getFloat("ta0", c); err == nil {
			v.TPLL.A0 = x
		}
		if x, err := getFloat("ta1", c); err == nil {
			v.TPLL.A1 = x
		}
		if x, err := getFloat("fa0", c); err == nil {
			v.FPLL.A0 = x
		}
		if x, err := getFloat("fa1", c); err == nil {
			v.FPLL.A1 = x
		}
		go v.afcthread()
		c.JSON(http.StatusOK, Response{"AFC on", nil})
	} else {
		v.Afc = false
		c.JSON(http.StatusOK, Response{"AFC off", nil})
	}
}

func (v *LTEView) afcthread() {
	afchandle := lte.NewAFC(v.view, false, 8)
	v.afcBackup()
	v.FailCount = 0
	v.OnTrace = false
	for v.Afc {
		afchandle.ChangeCid(v.Cid)
		if !v.OnTrace {
			v.sync(afchandle)
		} else {
			v.trace(afchandle)
		}
	}
}
func (v *LTEView) setdt(dt float64) {
	old := v.view.GetDt()
	v.view.SetDt(dt - old)
}
func (v *LTEView) setdf(df float64) {
	old := v.view.GetDf()
	v.view.SetDf(df - old)
}
func (v *LTEView) trace(afchandle *lte.AFC) {
	times := 100
	// if v.FailCount < -10 {
	// 	times = -v.FailCount * 2
	// }
	for i := 0; i < times; i++ {
		tf := afchandle.Trace()
		v.setdt(-tf + v.DT)
		v.view.Reset()
		v.view.Run(1)
	}
	pos := afchandle.Peak()
	log.Info(v.Name, " pos @", pos, " f: ", v.FailCount, " s: ", afchandle.TPLLState())

	dF := afchandle.FreqOffset()
	if math.Abs(dF) > 10 {
		// Frequency offset > 10Hz
		dF = afchandle.CarrierOne(false)
	} else {
		// Frequency offset < 10Hz
		v.view.SetDf(dF / 2.)
		v.view.Reset()
	}
	log.Info(v.Name, " df @", dF, " f: ", v.FailCount)
	// Backup
	if math.Abs(dF) < 1. && math.Abs(float64(pos)-9600.) < 2 && v.FailCount < -30 {
		v.afcBackup()
	}
	// Adjust fail counter
	if math.Abs(float64(pos)-9600.) > 5 || math.Abs(dF) > 10 {
		if v.FailCount < 0 {
			v.FailCount = 0
		}
		v.FailCount++
	} else {
		if v.FailCount > -100 {
			v.FailCount--
		}
	}
	// Reset frame start
	if v.FailCount > 3 {
		v.view.FrameStartAdjust(int64(9600 + pos))
		v.view.HardReset()
		v.view.Reset()
		log.Info(v.Name, " reset frame start point")
	}
	if v.FailCount > 10 {
		v.OnTrace = false
		v.afcRollback()
		v.FailCount = 0
		log.Info(v.Name, " return to sync mode")
	}
}

func (v *LTEView) sync(afchandle *lte.AFC) {
	t, ok := afchandle.Timing()
	if ok {
		v.Timing = t
		log.Info(v.Name)
		if !afchandle.AdjustPPMT(&t) {
			v.FailCount++
			if v.FailCount > 3 {
				v.afcRollback()
				v.FailCount = 0
			}
		} else {
			if v.FailCount > -3 {
				v.FailCount--
			}
		}
		dF := afchandle.CarrierOne(false)
		if math.Abs(dF) < 10. && v.FailCount <= -3 {
			v.afcBackup()
			v.OnTrace = true
			afchandle.Reset(v.TPLL, v.FPLL)
		}
	}
}
func (v *LTEView) afcBackup() {
	v.PPM, v.DFR = v.view.GetTuner()
	v.DF = v.view.GetDf()
	v.DT = v.view.GetDt()
}

func (v *LTEView) afcRollback() {
	v.view.SetTuner(v.PPM, v.DFR)
	old := v.view.GetDf()
	v.view.SetDf(v.DF - old)
	old = v.view.GetDt()
	v.view.SetDt(v.DT - old)
}

func (v *LTEView) ResetPPM(c *gin.Context) {
	v.view.ResetPPM()
	v.BeReset = true
	v.afcBackup()
	c.JSON(http.StatusOK, Response{"reset ppm", nil})
}
